package cn.zyb.user.service.impl;

import cn.zyb.basic.contant.VerifyCodeConstant;
import cn.zyb.basic.exception.BusinessException;
import cn.zyb.basic.service.Impl.BaseServiceImpl;
import cn.zyb.basic.util.AjaxResult;
import cn.zyb.basic.util.HttpUtil;
import cn.zyb.basic.util.Md5Utils;
import cn.zyb.basic.util.StrUtils;
import cn.zyb.system.domain.Menu;
import cn.zyb.system.mapper.MenuMapper;
import cn.zyb.system.mapper.PermissionMapper;
import cn.zyb.user.contant.WxConstants;
import cn.zyb.user.domain.Logininfo;
import cn.zyb.user.domain.User;
import cn.zyb.user.domain.Wxuser;
import cn.zyb.user.dto.LoginDto;
import cn.zyb.user.jwt.JwtUtils;
import cn.zyb.user.jwt.LoginData;
import cn.zyb.user.jwt.RsaUtils;
import cn.zyb.user.mapper.LogininfoMapper;
import cn.zyb.user.mapper.UserMapper;
import cn.zyb.user.mapper.WxuserMapper;
import cn.zyb.user.service.ILogininfoService;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.security.PrivateKey;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * 业务实现类：
 */
@Service
public class LogininfoServiceImpl extends BaseServiceImpl<Logininfo> implements ILogininfoService {
  @Autowired
  private LogininfoMapper logininfoMapper;

  @Autowired
  private RedisTemplate redisTemplate;

  @Autowired
  private WxuserMapper wxuserMapper;

  @Autowired
  private UserMapper userMapper;

  @Autowired
  private PermissionMapper permissionMapper;

  @Autowired
  private MenuMapper menuMapper;

  @Override
  public AjaxResult accountLogin(LoginDto loginDto) {
    String account = loginDto.getAccount();
    String inputPwd = loginDto.getCheckPass();
    Integer type = loginDto.getType();
    //1.校验 - 空值校验
    if(StringUtils.isEmpty(account) || StringUtils.isEmpty(inputPwd)){
      throw  new BusinessException("登录信息不能为空!!!");
    }

    //2.校验 - 校验账号
    Logininfo logininfo = logininfoMapper.loadByAccount(loginDto);
    if(logininfo == null){ //通过账号没有查询到信息 = 账号错误
      throw  new BusinessException("账号错误!!!");
    }

    //3.校验密码
    String dbPwd = logininfo.getPassword(); //数据库中加密的密码
    String inputMd5Pwd = Md5Utils.encrypByMd5(inputPwd + logininfo.getSalt());
    if(!dbPwd.equals(inputMd5Pwd)){
      throw  new BusinessException("密码错误!!!");
    }

    Map<String,Object> map = loginSuccessJwtHandler(logininfo);

    //6.响应数据
    return AjaxResult.me().setResultObj(map);
  }

  private Map<String, Object> loginSuccessJwtHandler(Logininfo logininfo) {
    try {
      Map<String,Object> map = new HashMap<>();
      //细节：如果是管理员才需要加四个数据，如果是用户只需要：登录信息对象，jwt串【1,4】
      //一：想办法获取四个数据
      //1.登录信息对象 - 在参数中
      map.put("logininfo",logininfo);
      LoginData loginData = new LoginData();
      loginData.setLogininfo(logininfo);
      if(logininfo.getType()==0){//后台管理员
        //2.当前登录人的所有权限的sn集合 - 昨天写过了 - 直接调用方法
        List<String> permissions = permissionMapper.loadPermissionSnByLogininfoId(logininfo.getId());
        map.put("permissions",permissions);
        loginData.setPermissions(permissions);
        //3.当前登录人的所有二级菜单及其它的父级菜单【前端需要】 - 自己写 - 难点1
        List<Menu> menus = menuMapper.loadMenusByLogininfoId(logininfo.getId());
        map.put("menus",menus);
        loginData.setMenus(menus);
      }
      //4.将以上三个数据装到LoginData  - 使用工具类进行加密【1 2】 - jwt串
      PrivateKey privateKey = RsaUtils.getPrivateKey(JwtUtils.class.getClassLoader().getResource("auth_rsa.pri").getFile());
      String jwtToken = JwtUtils.generateTokenExpireInMinutes(loginData, privateKey, 30000);//没有做刷新过期时间

      //二：将四个数据装到map进行返回。疑问：jwt串里面不是已经有登录信息，权限和菜单了吗？ - 前端没有做解密
      map.put("token",jwtToken);
      return map;
    } catch (Exception e) {
      e.printStackTrace();
    }
    return  null;
  }

  @Override
  public AjaxResult wechatLogin(String code) {
    //1.准备发送第二个请求的参数：
    String url = WxConstants.GET_ACK_URL.replace("APPID", WxConstants.APPID)
      .replace("SECRET", WxConstants.SECRET)
      .replace("CODE", code);
    //2.发送请求 - 返回json格式的数据
    String jsonStr = HttpUtil.httpGet(url);

    //3.将json格式的数据转成json对象 - 为了获取里面的token和openid
    JSONObject jsonObject = JSONObject.parseObject(jsonStr);
    String accessToken = jsonObject.getString("access_token");
    String openId = jsonObject.getString("openid");
    //4.通过openid查询数据库
    Wxuser wxuser = wxuserMapper.loadByOpenId(openId);
    //4.1.查询到了并且user_id有值 = 以前绑定过 = 直接免密登录
    if(wxuser!=null && wxuser.getUserId()!=null && userMapper.loadById(wxuser.getUserId())!=null){
      //免密登录 = 不需要通过页面输入账号密码就直接登录：token，logininfo
      //4.登录成功将登录信息保存在redis - 方便后续判断时候已经登陆过
      User user = userMapper.loadById(wxuser.getUserId());
      Logininfo logininfo = logininfoMapper.loadById(user.getLogininfoId());

      Map<String,Object> map = loginSuccessJwtHandler(logininfo);

      //6.响应数据
      return AjaxResult.me().setResultObj(map);
    }else{
      //4.2.没有查询到响应给前端进行跳转 = 设置success=false，并且将token和openid传递给前端
      String param = "?accessToken="+accessToken+"&openId="+openId;
      return AjaxResult.me().setSuccess(false).setResultObj(param);
    }
  }

  @Override
  public AjaxResult wechatBinder(Map<String, String> map) {
    String phone = map.get("phone");
    String verifyCode = map.get("verifyCode");
    String accessToken = map.get("accessToken");
    String openId = map.get("openId");
    //一：校验 - 空值校验
    if(StringUtils.isEmpty(phone) || StringUtils.isEmpty(verifyCode)){
      throw new BusinessException("信息不能为空!!!");
    }
    //二：校验 - 手机验证码是否过期 是否正确 [业务🗡:phone 验证码:时间戳]
    Object obj = redisTemplate.opsForValue().get(VerifyCodeConstant.BINDER_CODE_PREFIX + phone);
    if(obj==null){
      throw new BusinessException("验证码过期!!!");
    }
    //redis保存的验证码  和 用户输入的验证码
    if(!obj.toString().split(":")[0].equals(verifyCode)){
      throw new BusinessException("验证码错误!!!");
    }

    //三：通过ack和openid发送第三个请求
    String url = WxConstants.GET_USER_URL.replace("ACCESS_TOKEN", accessToken)
      .replace("OPENID", openId);
    String jsonStr = HttpUtil.httpGet(url);
    //四：处理响应的数据：json字符串 - json对象 - 获取wxUser的信息
    Wxuser wxuser = jsonStr2Wxuser(jsonStr);

    //五：绑定用户：将WxUser对象【第三个请求中获取】 与 User对象【通过手机创建】进行绑定
    User user = userMapper.loadByPhone(phone);
    if(user==null){//根据电话没有找到user对象
      //通过phone创建User对象
      user = phone2User(phone);
      Logininfo logininfo = user2Logininfo(user);

      logininfoMapper.save(logininfo);
      user.setLogininfoId(logininfo.getId());
      userMapper.save(user);
    }

    //user一定会有。并且user_id也有
    wxuser.setUserId(user.getId());
    wxuserMapper.save(wxuser);

    Logininfo logininfo = logininfoMapper.loadById(user.getLogininfoId());

    Map<String,Object> mapTemp = loginSuccessJwtHandler(logininfo);

    return new AjaxResult().setResultObj(mapTemp);
  }

  private Logininfo user2Logininfo(User user) {
    Logininfo logininfo = new Logininfo();
    BeanUtils.copyProperties(user,logininfo);
    logininfo.setType(1);
    return logininfo;
  }

  private User phone2User(String phone) {
    User user = new User();
    user.setUsername(phone);
    user.setPhone(phone);
    String salt = StrUtils.getComplexRandomString(32);
    //随机密码
    String randomPwd = StrUtils.getComplexRandomString(6);
    String md5Pwd = Md5Utils.encrypByMd5(randomPwd + salt);
    user.setSalt(salt);
    user.setPassword(md5Pwd);
    user.setState(1);

    return user;
  }

  private Wxuser jsonStr2Wxuser(String jsonStr) {
    JSONObject jsonObject = JSONObject.parseObject(jsonStr);
    Wxuser wxuser = new Wxuser();
    wxuser.setOpenid(jsonObject.getString("openid"));
    wxuser.setNickname(jsonObject.getString("nickname"));
    wxuser.setSex(jsonObject.getInteger("sex"));
    wxuser.setHeadimgurl(jsonObject.getString("headimgurl"));
    wxuser.setUnionid(jsonObject.getString("unionid"));

    String country = jsonObject.getString("country");
    String province = jsonObject.getString("province");
    String city = jsonObject.getString("city");

    wxuser.setAddress(country+province+city);
    return wxuser;
  }
}
